package com.cx.property;

import com.cx.utils.CacheConsts;
import com.google.common.collect.Lists;
import io.lettuce.core.RedisURI;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springside.modules.utils.collection.CollectionUtil;
import redis.clients.jedis.JedisShardInfo;

import java.time.Duration;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @author Alan Shu
 */

@ConfigurationProperties(
        prefix = "spring.redis"
)
@Setter
@Getter
public class LettuceProperties extends RedisProperties {

    /**
     * 项目名称
     */
    private String proName = "njr";

    /**
     * 缓存模式
     */
    private String cacheMode = "REACTIVE_PIPELINE";

    /**
     * 密码（默认值防止无密码报错）
     */
    private String password = "";

    /**
     * 一级缓存名称
     */
    private String cacheLevel1 = "cache1st";

    /**
     * 二级缓存名称
     */
    private String cacheLevel2 = "cache2nd";

    /**
     * 一级缓存过期时间（秒）
     */
    private long cacheLevel1Ttl = 86400L; //过期时间1天

    /**
     * 二级缓存过期时间（秒）
     */
    private long cacheLevel2Ttl = 86400L; //过期时间1天

    /**
     * 序列化类型
     */
    private String serializeType = CacheConsts.SERIALIZE_TYPE_FST;

    /**
     * 全局命令操作超时时间
     */
    private long timeoutOptions = 1L;

    /**
     * 队列大小
     */
    private int requestQueueSize = 20;

    /**
     * 是否开启通道模式
     */
    private boolean openPipeline = false;

    /**
     * 异步结果超时时间（毫秒）
     */
    private long asyncResultTimeoutInMillis;

    /**
     * I/O 线程池大小
     */
    private int ioThreadPoolSize = 4;

    /**
     * I/O 线程池大小
     */
    private int computationThreadPoolSize = 4;

    private LettuceProperties.Pool pool;

    /**
     * 分片: 第一台
     */
    private JedisShardInfo shardInfo;

    /**
     * 分片：后三台
     */
    private LettuceProperties.Shard shard;

    private LettuceProperties.Cluster cluster;

    public LettuceProperties() {
        super();
    }

    @Setter
    @Getter
    public static class Pool extends RedisProperties.Pool {
        private boolean testOnCreate = false;
        private boolean testOnAcquire = true;
        private boolean testOnRelease = false;

        public Pool() {
            super();
        }
    }

    /**
     * 分片模式：加载配置时，其实只要多数节点配置上就可以，其它自己发现
     * 注意：info(1～3)都不能为空
     */
    @Setter
    @Getter
    public static class Shard {

        private JedisShardInfo info1;
        private JedisShardInfo info2;
        private JedisShardInfo info3;
    }

    public List<JedisShardInfo> shards(){
        return Arrays.asList(
                shard.getInfo1(),
                shard.getInfo2(),
                shard.getInfo3());
    }

    @Setter
    @Getter
    public static class Cluster extends RedisProperties.Cluster{

        private String mode;
        private String readFromStr;

        public Cluster() {
            super();
        }
    }

    public String cacheLevel1Pre(){
        return this.proName + CacheConsts.SP + this.cacheLevel1 + CacheConsts.SP;
    }

    public String cacheLevel2Pre(){
        return this.proName + CacheConsts.SP + this.cacheLevel2 + CacheConsts.SP;
    }

    public RedisURI getRedisURI(){
        RedisURI.Builder builder = RedisURI.Builder
        .redis(getHost(), getPort())
        .withClientName(proName)
        .withSsl(false)
        .withStartTls(false)
        .withVerifyPeer(false)
        .withDatabase(getDatabase())
        .withPassword(getPassword())
        .withTimeout(Duration.ofMillis(getTimeout()));

        if(null != getSentinel()) {
            builder.withSentinelMasterId(getSentinel().getMaster());
            List<String> nodes = Arrays.asList(getSentinel().getNodes().split(","));
            Iterator<String> iterator = nodes.iterator();
            while (iterator.hasNext()){
                String[] hostAndPort = iterator.next().split(CacheConsts.SP);
                builder.withSentinel(hostAndPort[0], Integer.valueOf(hostAndPort[1]));
            }
        }

        return builder.build();
    }

    public List<RedisURI> clusterNodes(){

        List<RedisURI> list = Lists.newArrayListWithCapacity(6);
        if(null == getCluster() || CollectionUtil.isEmpty(getCluster().getNodes())) {
            list.add(getRedisURI());
        }else {
            getCluster().getNodes().iterator().forEachRemaining(node -> {
                String[] hostAndPort = node.split(CacheConsts.SP);
                list.add(new RedisURI(hostAndPort[0], Integer.valueOf(hostAndPort[1]), Duration.ofMillis(getTimeout())));
            });
        }

        return list;
    }
}